package com.xcb.spring.beans.factory;

import com.xcb.spring.aop.AspectJAwareAdvisorAutoProxyCreator;
import com.xcb.spring.beans.BeanPostProcessor;
import com.xcb.spring.beans.BeanDefinition;
import com.xcb.spring.beans.InitializingBean;
import com.xcb.spring.beans.ScopeEnum;
import com.xcb.spring.beans.aware.BeanNameAware;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 */
public abstract class AbstractBeanFactory implements BeanFactory {

	private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

	private final List<String> beanDefinitionNames = new ArrayList<String>();

	private List<BeanPostProcessor> beanPostProcessors = new ArrayList<BeanPostProcessor>();

	private final Map<String,Object> singleBeans = new ConcurrentHashMap<String, Object>();

	private final Map<String,Object> earlyBeans = new ConcurrentHashMap<String, Object>();

	private final Map<String,ObjectFactory> beansfactory = new ConcurrentHashMap<String, ObjectFactory>();

	private final Set<String> creatingBeans = new HashSet<String>(255);

	private final static String FULL_CYCLE = "FULL_CYCLE";

	@Override
	public Object getBean(String name) throws Exception {
		Object obj = getSingleton(name);
		if(obj!=null){
			return obj;
		}
		BeanDefinition beanDefinition = beanDefinitionMap.get(name);
		if (beanDefinition == null) {
			throw new IllegalArgumentException("No bean named " + name + " is defined");
		}
		obj = doCreateBean(name,beanDefinition);
		obj = initializeBean(obj, name);

		// 由于递归完后还是原实例，， 所以要从二级缓存中拿到proxy 。
		if(earlyBeans.containsKey(name)){
			obj = earlyBeans.get(name);
		}
		singleBeans.put(name,obj);
		return obj;
	}

	protected Object getSingleton(String name){
		Object obj = singleBeans.get(name);
		if(obj != null){
			return obj;
		}
		//循环依赖
		if(creatingBeans.contains(name)){
			//先从二级缓存中拿,
			if(earlyBeans.containsKey(name)){
				return earlyBeans.get(name);
			}
			//从三级缓存中拿
			ObjectFactory objectFactory = beansfactory.get(name);
			if(objectFactory != null){
				try {
					obj = objectFactory.getObject(name);
					if(obj != null){
						//存入二级缓存
						earlyBeans.put(name,obj);
					}
					return obj;
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	protected Object initializeBean(Object bean, String name) throws Exception {
		BeanDefinition beanDefinition = beanDefinitionMap.get(name);
		if(beanDefinition.getArguments(FULL_CYCLE)!=null){
			return bean;
		}
		for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
			bean = beanPostProcessor.postProcessBeforeInitialization(name,bean);
		}
		for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
			if(beanPostProcessor instanceof BeanPostProcessor){
				bean = beanPostProcessor.postProcessAfterInitialization(name,bean);
			}
		}
		return bean;
	}


	protected Object createBeanInstance(BeanDefinition beanDefinition) throws Exception {
		return beanDefinition.getBeanClass().newInstance();
	}

	public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {
		beanDefinitionMap.put(name, beanDefinition);
		beanDefinitionNames.add(name);
	}

	public void preInstantiateSingletons() throws Exception {
		for (Iterator it = this.beanDefinitionNames.iterator(); it.hasNext();) {
			String beanName = (String) it.next();
			BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
			if (beanDefinition.getScope().equals(ScopeEnum.singleton)) {
				getBean(beanName);
			}
		}
	}

	protected Object doCreateBean(String beanName, final BeanDefinition beanDefinition) throws Exception {
		//标记为正在创建中
		if(!creatingBeans.contains(beanName)){
			creatingBeans.add(beanName);
		}
		final Object bean = createBeanInstance(beanDefinition);
		//提前暴露
		beansfactory.put(beanName, new ObjectFactory() {
			@Override
			public Object getObject(String name) throws Exception {
				Object initializeBean = initializeBean(bean, name);
				beanDefinition.setArguments(FULL_CYCLE,1);
				return initializeBean;
			}
		});
		//设置属性
		applyPropertyValues(bean, beanDefinition);
		// Aware回调
		if (bean instanceof BeanNameAware) {
			((BeanNameAware)bean).setBeanName(beanName);
		}

		// 初始化
		if (bean instanceof InitializingBean) {
			((InitializingBean)bean).afterPropertiesSet();
		}
		return bean;
	}

	protected void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception {

	}

	public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) throws Exception {
		this.beanPostProcessors.add(beanPostProcessor);
	}

	public List getBeansForType(Class type) throws Exception {
		List beans = new ArrayList<Object>();
		for (String beanDefinitionName : beanDefinitionNames) {
			if (type.isAssignableFrom(beanDefinitionMap.get(beanDefinitionName).getBeanClass())) {
				beans.add(getBean(beanDefinitionName));
			}
		}
		return beans;
	}

}
